package com.bbf.tio.test;

import com.bbf.tio.common.TioConstants;
import com.bbf.tio.common.TioPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.intf.ClientAioHandler;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;

import java.nio.ByteBuffer;

/**
 * TIO消息处理类
 * @author BBF
 */
public class TioClientHandler implements ClientAioHandler {
  private static final Logger LOGGER = LoggerFactory.getLogger(TioClientHandler.class);

  private static final TioPacket heartbeatPacket = new TioPacket();

  /**
   * 为兼容Socket其他类型，重写encode，不设置消息头
   * 编码：把业务消息包编码为可以发送的ByteBuffer
   * 总的消息结构：消息头 + 消息体
   * 消息头结构：4个字节，存储消息体的长度
   * 消息体结构：对象的json串的byte[]
   */
  @Override
  public ByteBuffer encode(Packet packet,
                           GroupContext groupContext,
                           ChannelContext channelContext) {
    LOGGER.info("[TioClientHandler.encode]packet = {}", packet.toString());
    TioPacket tioPacket = (TioPacket) packet;
    byte[] body = tioPacket.getBody();
    int bodyLen = 0;
    if (body != null) {
      bodyLen = body.length;
    }
    //ByteBuffer的总长度是 = 消息头的长度 + 消息体的长度
    //int allLen = TioConstants.HEADER_LENGTH + bodyLen;
    ByteBuffer buffer = ByteBuffer.allocate(bodyLen);
    //设置字节序
    buffer.order(groupContext.getByteOrder());
    //写入消息头----消息头的内容就是消息体的长度
    //buffer.putInt(allLen);
    //写入消息体
    if (body != null) {
      buffer.put(body);
    }
    return buffer;
  }

  /**
   * 解码：把接收到的ByteBuffer，解码成应用可以识别的业务消息包
   * 总的消息结构：消息头 + 消息体
   * 消息头结构：4个字节，存储消息体的长度
   * 消息体结构：对象的json串的byte[]
   */
  @Override
  public TioPacket decode(ByteBuffer buffer,
                          ChannelContext channelContext)
      throws AioDecodeException {
    int readableLength = buffer.limit() - buffer.position();
    //收到的数据组不了业务包，则返回null以告诉框架数据不够
    if (readableLength < TioConstants.HEADER_LENGTH) {
      return null;
    }
    //读取消息体的长度
    int bodyLength = buffer.getInt();
    //数据不正确，则抛出AioDecodeException异常
    if (bodyLength < 0) {
      throw new AioDecodeException(String.format("bodyLength [%d] is not right, remote: %s",
          bodyLength, channelContext.getClientNode().toString()));
    }
    //收到的数据是否足够组包
    int isDataEnough = readableLength - TioConstants.HEADER_LENGTH;
    // 不够消息体长度(剩下的buffer组不了消息体)
    if (isDataEnough < 0) {
      return null;
    } else {
      //组包成功
      TioPacket imPacket = new TioPacket();
      if (isDataEnough > 0) {
        byte[] dst = new byte[isDataEnough];
        buffer.get(dst);
        imPacket.setBody(dst);
      }
      return imPacket;
    }
  }

  /**
   * 处理消息
   */
  @Override
  public void handler(Packet packet,
                        ChannelContext channelContext) {
    TioPacket tioPacket = (TioPacket) packet;
    byte[] body = tioPacket.getBody();
    if (body != null) {
      String str = new String(body, TioConstants.CHARSET_UTF8);
      LOGGER.info("[TioClientHandler.handler]TIO客户端收到消息：--{}--", str);
    }
  }

  /**
   * 此方法如果返回null，框架层面则不会发心跳；如果返回非null，框架层面会定时发本方法返回的消息包
   */
  @Override
  public TioPacket heartbeatPacket() {
    return heartbeatPacket;
  }
}
